]> AND Private Git Repository - these_gilles.git/blob - DOCS/Scalable and Interactive Segmentation and Visualization of Neural Processes in EM Datasets_files/3839767.js
Logo AND Algorithmique Numérique Distribuée

Private GIT Repository
7 avril pour jury
[these_gilles.git] / DOCS / Scalable and Interactive Segmentation and Visualization of Neural Processes in EM Datasets_files / 3839767.js
1 jQuery(function($j) {
2       var formState = {
3           overrideBackends: false,
4           backends: {}
5       };
6       
7       // Name of the cookie
8       var cookieName;
9       
10       // Mostly just for debugging, store the cookie string value here
11       // rather than in the sub-function scope
12       var cookieStr;
13       
14       // An object representation of the cookie.  This is converted from the
15       // XML cookie value on init.  The form controls will manipulate this,
16       // and when the user clicks "Go", this will be converted back into
17       // XML.
18       var cookieObj;
19
20       ///////////////////////////////////////////////////////////////////////////////
21       function cbChanged(event) {
22           //console.info("Event caught: " + event);
23           var target = $j(event.target);
24           var id = target.attr("id");
25           var value = target.attr("value");
26           var checked = target.attr("checked");
27           /*console.info("target id: '" + id + 
28                        "', value: '" + value + 
29                        "', checked: '" + checked + "'");*/
30           
31           
32           if (id == "besetsel-cb") {
33               if (checked) {
34                   $j("#besetsel-sel").removeAttr("disabled");
35                   besetSelFormToObj();
36               }
37               else {
38                   $j("#besetsel-sel").attr("disabled", 1);
39                   delete cookieObj.besetName;
40               }
41           }
42           else if (id == "besetsel-sel") {
43               besetSelFormToObj();
44           }
45           else {
46               var m;
47               if (m = id.match(/besetsel-be-(.*?)-cb/)) {
48                   var backend = m[1];
49                   //console.info(">>>backend checkbox:  " + backend);
50                   if (checked) {
51                       $j("#besetsel-be-" + backend + "-text").removeAttr("disabled");
52                       beUrlFormToObj(backend);
53                   }
54                   else {
55                       $j("#besetsel-be-" + backend + "-text").attr("disabled", 1);
56                       delete cookieObj.backendUrls[backend];
57                   }
58               }
59               else if (m = id.match(/besetsel-be-(.*?)-text/)) {
60                   backend = m[1];
61                   //console.info(">>>backend text:  " + backend);
62                   beUrlFormToObj(backend);
63               }
64           }
65           
66           // PMC-11784 and PMC-11785.
67           // This fixes a nasty IE bug.  It causes a slight flash when the user
68           // clicks a checkbox, but it works.
69           if (jQuery.browser.msie){
70               target.hide();
71               window.setTimeout( function(){ target.show();}, 0 );
72           }
73           
74       }
75
76       ///////////////////////////////////////////////////////////////////////////////
77       // besetSelFormToObj()
78       // This is called by a couple of event handlers and decodes the
79       // currently selected BESet (in the drop-down form) and sets the
80       // cookieObj.besetName accordingly.
81
82       function besetSelFormToObj()
83       {
84           cookieObj.besetName = $j("#besetsel-sel").val();
85       }
86
87       ///////////////////////////////////////////////////////////////////////////////
88       // beUrlFormToObj(backend)
89       // This is similar, and takes care of reading the text value from the
90       // form and stuffing it into the object
91
92       function beUrlFormToObj(backend) {
93           var value = $j("#besetsel-be-" + backend + "-text").attr("value");
94           if (value) cookieObj.backendUrls[backend] = value;
95       }
96
97       ///////////////////////////////////////////////////////////////////////////////
98       function init() {
99           if ($j("#besetsel-form").length < 1)
100           {
101               return;
102           }
103           
104           cookieName = $j("#besetsel-form").attr("cookieName");
105           cookieObj = cookieXmlToJson(cookieName);
106           initFormState();
107
108           // Set event handers
109           $j("#besetsel-form .besetsel-control").change(function(event) {
110               cbChanged(event);
111           });
112           $j("#besetsel-go-button").click(function(event) {
113               goButton(event);
114           });
115           $j("#besetsel-reset-button").click(function(event) {
116               resetButton(event);
117           });
118           
119           // This "pullout" might be empty, in the case of the BESet being
120           // selected by path segment instead of cookie.  In that case, the
121           // tab acts as a watermark, just to identify the BESet, and we
122           // don't want to allow it to be "pulled out".  So we'll set the
123           // width to 0 in that case.
124           var w = $j("#besetsel-go-button").length > 0 ? "400px" : "0px";
125
126           // Put it into the sidecontent pullout
127           $j("#besetsel-form").sidecontent({
128               /*classmodifier: "besetsel",*/
129               attachto: "rightside",
130               width: w,
131               opacity: "0.8",
132               pulloutpadding: "5",
133               textdirection: "vertical",
134               clickawayclose: 0,
135               titlenoupper: 1
136           });
137           
138           var pulloutColor = $j("#besetsel-form").attr("pulloutColor");
139           //alert("color is " + pulloutColor);
140           $j("#besetsel-form").data("pullout").css("background-color", pulloutColor || '#663854');
141           
142           if ($j("#besetsel-go-button").size() > 0) {
143               $j("#besetsel-form").data("pullout").css({
144                   "border-top": "ridge gray 5px",
145                   "border-bottom": "ridge gray 5px",
146                   "border-left": "ridge gray 5px"
147               });
148           }
149       }
150
151       ///////////////////////////////////////////////////////////////////////////////
152       // goButton(event)
153       // Handle the user-click of the "Go!" button.
154       
155       function goButton(event) {
156           // Convert the object into XML
157           var cookieXml = "<Backends><BESet" + ( cookieObj.besetName ? (" name='" + cookieObj.besetName + "'>") : ">" );
158           for (var backend in cookieObj.backendUrls) {
159               //console.info("+++ backend " + backend);
160               cookieXml += 
161                 "<Backend name='" + backend + "'>" + xmlEscape(cookieObj.backendUrls[backend]) + "</Backend>";
162           }
163           cookieXml += "</BESet></Backends>";
164           //console.info(cookieXml);
165           
166           // Set the cookie
167           document.cookie = cookieName + "=" + encodeURIComponent(cookieXml) +
168                             "; max-age=604800" +
169                             "; path=/" +
170                             "; domain=nih.gov";
171           // Reload the page
172           window.location.reload();
173       }
174       
175       ///////////////////////////////////////////////////////////////////////////////
176       // resetButton(event)
177       // Handle the user-click of the "Reset" button.
178       // Does the same thing as "Go!", but sets the cookie to the empty string.
179
180       function resetButton(event) {
181           // Clear the cookie
182           document.cookie = cookieName + "=" + 
183                             "; max-age=604800" +
184                             "; path=/" +
185                             "; domain=nih.gov";
186           // Reload the page
187           window.location.reload();
188       }
189       
190       ///////////////////////////////////////////////////////////////////////////////
191       function xmlEscape(str) {
192           str = str.replace(/\&/g, '&amp;')
193                    .replace(/\</g, '&lt;')
194                    .replace(/\>/g, '&gt;')
195                    .replace(/\"/g, '&quot;')
196                    .replace(/\'/g, '&apos;');
197           return str;
198       }
199
200       ///////////////////////////////////////////////////////////////////////////////
201       // This function reads the cookie value and initializes the form state
202       // Don't assume anything about the form state -- redo everything.
203       function initFormState() {
204
205           var besetName = cookieObj.besetName;
206
207           if (!besetName) {
208               $j("#besetsel-cb").removeAttr("checked");
209               $j("#besetsel-sel").attr("disabled", 1);
210           }
211           else {
212               var selBESet = $j("#besetsel-opt-" + besetName);
213               if (selBESet.length != 0) {
214                   $j("#besetsel-cb").attr("checked", 1);
215                   $j("#besetsel-sel").removeAttr("disabled");
216                   selBESet.attr("selected", 1);
217               }
218               else {
219                   $j("#besetsel-cb").removeAttr("checked");
220                   $j("#besetsel-sel").attr("disabled", 1);
221               }
222           }
223           
224           // Foreach backend in the form
225           $j(".besetsel-be-cb").each(function(i) {
226               var id = $j(this).attr("id");
227               var beName = id.match(/besetsel-be-(.*?)-cb/)[1];
228               //console.info("### backend, id is '" + id + "', beName is '" + beName + "'");
229               if (!beName) return;
230               
231               // See if there's a corresponding element in the cookie
232               if (!cookieObj.backendUrls ||
233                   !cookieObj.backendUrls[beName]) {
234                   //console.info("Didn't find " + beName);
235                   $j("#besetsel-be-" + beName + "-cb").removeAttr("checked");
236                   $j("#besetsel-be-" + beName + "-text").attr("disabled", 1);
237               }
238               else {
239                   //console.info("Found " + beName);
240                   $j("#besetsel-be-" + beName + "-cb").attr("checked", 1);
241                   var textbox = $j("#besetsel-be-" + beName + "-text");
242                   textbox.removeAttr("disabled");
243                   textbox.attr("value", cookieObj.backendUrls[beName]);
244               }
245           });
246       }
247       
248       ///////////////////////////////////////////////////////////////////////////////
249       // This gets the value of the <snapshot>_beset cookie, which is in XML, and turns it
250       // from this:
251       //   <BESet name='test'>
252       //     <BackendUrl backend='tagserver' url='bingo'/>
253       //     ...
254       //   </BESet>
255       // Into this (note that everything is optional):
256       //   { besetName: 'test',
257       //     backendUrls: {
258       //         tagserver: 'bingo', ... }
259       //   }
260       // If there is no cookie set or parsing fails, this returns {}.
261       
262       function cookieXmlToJson(cookieName) {
263           var cookieObj = {
264               backendUrls: {}
265           };
266
267           cookieStr = getCookie(cookieName);
268           //console.info("cookie value is '" + cookieStr + "'");
269
270           // Parse XML
271           try {
272               var cookieXml = $j(cookieStr);
273           }
274           catch(err) {
275               return cookieObj;
276           }
277           
278           var besetElem = cookieXml.find('BESet');
279           if (besetElem.length == 0) {
280               // No valid cookie value found.
281               return cookieObj;
282           }
283           
284           var besetName = besetElem.attr("name");
285           if (besetName) {
286               cookieObj.besetName = besetName; 
287           }
288           
289           var backends = besetElem.find("backend");
290           if (backends.length != 0) {
291               backends.each(function (i) {
292                   var e = $j(backends[i]);
293                   cookieObj.backendUrls[e.attr("name")] = e.text();
294                   //console.info("Setting " + e.attr("backend") + ": " + e.attr("url"));
295               })
296           }
297           
298           return cookieObj;
299       }
300
301       ///////////////////////////////////////////////////////////////////////////////
302       function getCookie(name) {
303           var allCookies = document.cookie;
304           //console.info("allCookies = " + allCookies);
305           var pos = allCookies.indexOf(name + "=");
306           if (pos != -1) {
307               var start = pos + (name + "=").length;
308               var end = allCookies.indexOf(";", start);
309               if (end == -1) end = allCookies.length;
310               return decodeURIComponent(allCookies.substring(start, end)); 
311           }
312           return "";
313       }
314         
315     init();
316     
317 });
318
319
320
321 ;
322 (function($)\r
323 {\r
324         // This script was written by Steve Fenton\r
325         // http://www.stevefenton.co.uk/Content/Jquery-Side-Content/\r
326         // Feel free to use this jQuery Plugin\r
327         // Version: 3.0.2\r
328         \r
329         var classModifier = "";\r
330         var sliderCount = 0;\r
331         var sliderWidth = "400px";\r
332         \r
333         var attachTo = "rightside";\r
334         \r
335         var totalPullOutHeight = 0;\r
336         \r
337         function CloseSliders (thisId) {\r
338                 // Reset previous sliders\r
339                 for (var i = 0; i < sliderCount; i++) {\r
340                         var sliderId = classModifier + "_" + i;\r
341                         var pulloutId = sliderId + "_pullout";\r
342                         \r
343                         // Only reset it if it is shown\r
344                         if ($("#" + sliderId).width() > 0) {\r
345 \r
346                                 if (sliderId == thisId) {\r
347                                         // They have clicked on the open slider, so we'll just close it\r
348                                         showSlider = false;\r
349                                 }\r
350 \r
351                                 // Close the slider\r
352                                 $("#" + sliderId).animate({\r
353                                         width: "0px"\r
354                                 }, 100);\r
355                                 \r
356                                 // Reset the pullout\r
357                                 if (attachTo == "leftside") {\r
358                                         $("#" + pulloutId).animate({\r
359                                                 left: "0px"\r
360                                         }, 100);\r
361                                 } else {\r
362                                         $("#" + pulloutId).animate({\r
363                                                 right: "0px"\r
364                                         }, 100);\r
365                                 }\r
366                         }\r
367                 }\r
368         }\r
369         \r
370         function ToggleSlider () {\r
371                 var rel = $(this).attr("rel");\r
372 \r
373                 var thisId = classModifier + "_" + rel;\r
374                 var thisPulloutId = thisId + "_pullout";\r
375                 var showSlider = true;\r
376                 \r
377                 if ($("#" + thisId).width() > 0) {\r
378                         showSlider = false;\r
379                 }\r
380 \r
381         CloseSliders(thisId);\r
382                 \r
383                 if (showSlider) {\r
384                         // Open this slider\r
385                         $("#" + thisId).animate({\r
386                                 width: sliderWidth\r
387                         }, 250);\r
388                         \r
389                         // Move the pullout\r
390                         if (attachTo == "leftside") {\r
391                                 $("#" + thisPulloutId).animate({\r
392                                         left: sliderWidth\r
393                                 }, 250);\r
394                         } else {\r
395                                 $("#" + thisPulloutId).animate({\r
396                                         right: sliderWidth\r
397                                 }, 250);\r
398                         }\r
399                 }\r
400                 \r
401                 return false;\r
402         };\r
403 \r
404         $.fn.sidecontent = function (settings) {\r
405         \r
406                 var config = {\r
407                         classmodifier: "sidecontent",\r
408                         attachto: "rightside",\r
409                         width: "300px",\r
410                         opacity: "0.8",\r
411                         pulloutpadding: "5",\r
412                         textdirection: "vertical",\r
413                         clickawayclose: false\r
414                 };\r
415                 \r
416                 if (settings) {\r
417                         $.extend(config, settings);\r
418                 }\r
419                 \r
420                 return this.each(function () {\r
421                 \r
422                         $This = $(this);\r
423                         \r
424                         // Hide the content to avoid flickering\r
425                         $This.css({ opacity: 0 });\r
426                         \r
427                         classModifier = config.classmodifier;\r
428                         sliderWidth = config.width;\r
429                         attachTo = config.attachto;\r
430                         \r
431                         var sliderId = classModifier + "_" + sliderCount;\r
432                         var sliderTitle = config.title;\r
433                         \r
434                         // Get the title for the pullout\r
435                         sliderTitle = $This.attr("title");\r
436                         \r
437                         // Start the totalPullOutHeight with the configured padding\r
438                         if (totalPullOutHeight == 0) {\r
439                                 totalPullOutHeight += parseInt(config.pulloutpadding);\r
440                         }\r
441 \r
442                         if (config.textdirection == "vertical") {\r
443                                 var newTitle = "";\r
444                                 var character = "";\r
445                                 for (var i = 0; i < sliderTitle.length; i++) {\r
446                                         character = sliderTitle.charAt(i).toUpperCase();\r
447                                         if (character == " ") {\r
448                                                 character = "&nbsp;";\r
449                                         }\r
450                                         newTitle = newTitle + "<span>" + character + "</span>";\r
451                                 }\r
452                                 sliderTitle = newTitle;\r
453                         }\r
454                         \r
455                         // Wrap the content in a slider and add a pullout                       \r
456                         $This.wrap('<div class="' + classModifier + '" id="' + sliderId + '"></div>').wrap('<div style="width: ' + sliderWidth + '"></div>');\r
457             var pullout = $('<div class="' + classModifier + 'pullout" id="' + sliderId + '_pullout" rel="' + sliderCount + '">' + sliderTitle + '</div>').insertBefore($("#" + sliderId));\r
458             \r
459             // Store reference to the tab element in parent \r
460             $This.data('pullout', pullout);\r
461                         \r
462                         if (config.textdirection == "vertical") {\r
463                                 $("#" + sliderId + "_pullout span").css({\r
464                                         display: "block",\r
465                                         textAlign: "center"\r
466                                 });\r
467                         }\r
468                         \r
469                         // Hide the slider\r
470                         $("#" + sliderId).css({\r
471                                 position: "absolute",\r
472                                 overflow: "hidden",\r
473                                 top: "0",\r
474                                 width: "0px",\r
475                                 zIndex: "1",\r
476                                 opacity: config.opacity\r
477                         });\r
478                         \r
479                         // For left-side attachment\r
480                         if (attachTo == "leftside") {\r
481                                 $("#" + sliderId).css({\r
482                                         left: "0px"\r
483                                 });\r
484                         } else {\r
485                                 $("#" + sliderId).css({\r
486                                         right: "0px"\r
487                                 });\r
488                         }\r
489                         \r
490                         // Set up the pullout\r
491                         $("#" + sliderId + "_pullout").css({\r
492                                 position: "absolute",\r
493                                 top: totalPullOutHeight + "px",\r
494                                 zIndex: "1000",\r
495                                 cursor: "pointer",\r
496                                 opacity: config.opacity\r
497                         })\r
498                         \r
499                         $("#" + sliderId + "_pullout").live("click", ToggleSlider);\r
500                         \r
501                         var pulloutWidth = $("#" + sliderId + "_pullout").width();\r
502                         \r
503                         // For left-side attachment\r
504                         if (attachTo == "leftside") {\r
505                                 $("#" + sliderId + "_pullout").css({\r
506                                         left: "0px",\r
507                                         width: pulloutWidth + "px"\r
508                                 });\r
509                         } else {\r
510                                 $("#" + sliderId + "_pullout").css({\r
511                                         right: "0px",\r
512                                         width: pulloutWidth + "px"\r
513                                 });\r
514                         }\r
515                         \r
516                         totalPullOutHeight += parseInt($("#" + sliderId + "_pullout").height());\r
517                         totalPullOutHeight += parseInt(config.pulloutpadding);\r
518                         \r
519                         var suggestedSliderHeight = totalPullOutHeight + 30;\r
520                         if (suggestedSliderHeight > $("#" + sliderId).height()) {\r
521                                 $("#" + sliderId).css({\r
522                                         height: suggestedSliderHeight + "px"\r
523                                 });\r
524                         }\r
525                         \r
526                         if (config.clickawayclose) {\r
527                                 $("body").click( function () {\r
528                                         CloseSliders("");\r
529                                 });\r
530                         }\r
531                         \r
532                         // Put the content back now it is in position\r
533                         $This.css({ opacity: 1 });\r
534                         \r
535                         sliderCount++;\r
536                 });\r
537                 \r
538                 return this;\r
539         };\r
540 })(jQuery);
541 ;
542 /* Override this file with one containing code that belongs on every page of your application */
543
544
545 ;
546 jQuery(function($) {
547     // Set event listener to scroll the nav poppers to the current page when opened
548     $("#source-link-top, #source-link-bottom").bind(
549         "ncbipopperopencomplete",
550         function() {
551             var dest = $(this).attr('href');
552             var selected_link = $(dest).find('.current-toc-entry');
553
554             if (selected_link.length > 0) 
555             {
556                 $(dest).scrollTo(selected_link, { offset: -100, duration:  400 });
557             }
558         }
559     );  
560 });
561
562
563 ;
564 // See PMC-7567 - Google suggestions
565 // This is an AJAX implementation of functionality that already exists in the backend.
566 // This Portal/AJAX implementation overrides the backend implementation.
567 //
568 // This JS looks for a span with id "esearch-result-number".  If it exists,
569 // then it does an ajax call to esearch (at the url specified in the @ref
570 // attribute) and gets the count.  It then replaces the contents of this
571 // span with that count.  Finally, it shows the outer div.
572 //
573 // An example of an esearch query url is
574 //     /entrez/eutils/esearch.fcgi?term=unemployed&db=pmc&rettype=count&itool=QuerySuggestion
575 // which would return something like this:
576 //   <eSearchResult>
577 //     <Count>10573</Count>
578 //   </eSearchResult>
579 //
580 // PMC-11350 - itool=QuerySuggestion query parameter is required to filter
581 // out such queries while calculating statistics.
582
583 jQuery(document).ready(
584     function() {
585         var $ = jQuery;
586
587         $("#esearch-result-number").each(
588             function() {
589                 var countSpan = $(this);
590                 var esearchUrl = countSpan.attr("ref");
591                 if (esearchUrl.length > 0) {
592                 
593                     $.ajax({
594                         type: "GET",
595                         url: esearchUrl,
596                         dataType: "xml",
597                         success: function(xml) {
598                             $(xml).find('Count').each(function(){
599                                 var count = $(this).text();
600
601                                 // Insert the count into the element content,
602                                 // and show the outer div.
603                                 countSpan.text(count);
604                                 $("div#esearchMessageArea").show();
605                             });
606                         }
607                     }); 
608
609                 }
610             }
611         );
612     }
613 );
614
615
616 ;
617
618 // This code uses JavaScript to build search URL and send search request directly to the database
619 // that is asking for the request. This works only for resources at a top-level URL.
620 // If JavaScript is turned off, or a redirect loop would occur, the search form's HTTP GET is
621 // allowed to continue.
622
623 if (typeof(jQuery) != 'undefined') {
624     (function($) {
625         // BK-5746.
626         // This is the default function that returns the search URL
627         // You can override this by defining NCBISearchBar_searchUrl.
628         var defaultSearchUrl = function() {
629             var db = $('#entrez-search-db');
630             var term = $('#term');
631             if (db && term && db[0] && term[0]) {
632             
633                 // The searchUrl is the selected database's data-search-uri attribute, if set; otherwise it's /dbname/.
634                 var searchUrl = $("option:selected", db[0]).attr('data-search-uri') || ("/" + db[0].value + "/");
635                 
636                 var termParam = 
637                     (term[0].value.replace(/^\s+/,'').length != 0) ?
638                         "?term=" + encodeURIComponent(term[0].value).replace(/%20/,'+') : 
639                         "";
640                         
641                 return searchUrl + termParam;
642             }
643         }
644         
645         function searchUrl() {
646             // If the user has overridden the URL function:
647             var url = "";
648             if (typeof(NCBISearchBar_customSearchUrl) != "undefined") {
649                 url = NCBISearchBar_customSearchUrl();
650             }
651             if (!url) {
652                 url = defaultSearchUrl();
653             }
654             return url;
655         }
656         
657         // Handle search submit request
658         function do_search() {
659         
660             var form = $('#entrez-search-form');
661             
662             // Disable crap portal-injected parameters so they are not sent to search URI
663             $('input[type="hidden"][name^="p$"]', form).each(function() {
664                 $(this).attr('disabled', 'disabled');
665             });
666             
667             // Get new search URL with term, etc.
668             var search_url = searchUrl();
669
670             // Log request            
671             search_ping();
672
673             // Use POST for big things, GET for everything else
674             if (search_url.length > 2000) {
675                 form.attr('method','POST');
676                 form.attr('action',search_url.replace(/\?.*/,''));
677             } else {
678                 window.location = search_url;
679                 return false;
680             }
681             return true;
682         }
683         
684         // Copied from Entrez...
685         function search_ping() {
686                 var cVals = ncbi.sg.getInstance()._cachedVals;
687         
688                 var searchDetails = {}
689                 searchDetails["jsEvent"] = "search";
690                 
691                 var app = cVals["ncbi_app"];
692                 var db = cVals["ncbi_db"];
693                 var pd = cVals["ncbi_pdid"];
694                 var pc = cVals["ncbi_pcid"];
695                 
696                 var sel = document.getElementById("entrez-search-db");
697                 var searchDB = sel.options[sel.selectedIndex].value;
698                 var searchText = document.getElementById("term").value;
699                 
700                 if( app ){ searchDetails["ncbi_app"] = app.value; }
701                 if( db ){ searchDetails["ncbi_db"] = db.value; }
702                 if( pd ){ searchDetails["ncbi_pdid"] = pd.value; }
703                 if( pc ){ searchDetails["ncbi_pcid"] = pc.value; }
704                 if( searchDB ){ searchDetails["searchdb"] = searchDB;}
705                 if( searchText ){ searchDetails["searchtext"] = searchText;}
706                 
707                 ncbi.sg.ping(searchDetails);
708         }
709         
710         // User function NCBISearchBar_handle_autocomp(), if defined, must handle search request, including submit, if any
711         function autocomp_select(event, sgData) {
712            var term = $('#term');
713            if (typeof(NCBISearchBar_handle_autocomp) != 'undefined') {
714               NCBISearchBar_handle_autocomp(event, sgData);
715            } else {
716               if (!term.val().trim()) {
717                  $('#term').val(sgData.optionSelected||sgData.userTyped);
718               }
719               
720               // Only explicitly trigger submit if user didn't.
721               if (sgData.optionIndex >= 0) {
722                  $('#entrez-search-form').submit();
723               }
724            }
725         }
726
727         $(document).ready(function() {
728
729             var db = $('#entrez-search-db');
730             var term = $('#term');
731             var form = $('#entrez-search-form');
732
733             db.removeAttr('disabled'); // Reenable if this is backbutton
734             
735             // Handle autocomplete events
736             term.bind("ncbiautocompleteenter", autocomp_select ).bind("ncbiautocompleteoptionclick", autocomp_select );
737             
738             // If form is submitted, handle POST (if necessary) and logging.
739             form.submit(do_search);
740             
741             // Turn autocomplete on or off depending on whether the new database
742             // has an autocomplete dict defined on the selected option.
743             $('#entrez-search-db').change(function() {
744                var acdict = $('#entrez-search-db option:selected').attr('data-ac-dict');
745                if (acdict) {
746                   $("#term").ncbiautocomplete("option","isEnabled",true).ncbiautocomplete("option","dictionary",acdict);
747                   console.info("Setting autocomplete dictionary to " + acdict);
748                } else {
749                    console.info("Disabling autocomplete dictionary");
750                   $("#term").ncbiautocomplete("turnOff");
751                }
752             });
753           }); // End document.ready
754     })(jQuery); // Close scope
755 };
756
757
758
759
760 ;
761 (function( $ ){ // pass in $ to self exec anon fn
762     var post_url = '/myncbi/session-state/';
763
764     // on page ready
765     $( function() {
766     
767         $('div.portlet, div.section').each( function() {
768
769             // get the elements we will need
770             var self = $(this);
771             var anchor = self.find('a.portlet_shutter');
772             var content = self.find('div.portlet_content, div.sensor_content');
773
774             // we need an id on the body, make one if it doesn't exist already
775             // then set toggles attr on anchor to point to body
776             var id = content.attr('id') || $.ui.jig._generateId('portlet_content');
777             
778             anchor.attr('toggles', id);
779             content.attr('id', id);
780
781             // initialize jig toggler with proper configs, then remove some classes that interfere with 
782             // presentation
783             var togglerOpen = anchor.hasClass('shutter_closed')  ?  false  :  true; 
784
785             anchor.ncbitoggler({
786                 isIcon: false,
787                 initOpen: togglerOpen 
788             }).
789                 removeClass('ui-ncbitoggler-no-icon').
790                 removeClass('ui-widget');
791
792             // get rid of ncbitoggler css props that interfere with portlet styling, this is hack
793             // we should change how this works for next jig release
794             anchor.css('position', 'absolute').
795                 css('padding', 0 );
796
797 /*            self.find( 'div.ui-helper-reset' ).
798                 removeClass('ui-helper-reset');
799
800             content.removeClass('ui-widget').
801                 css('margin', 0);*/
802
803             // trigger an event with the id of the node when closed
804             anchor.bind( 'ncbitogglerclose', function() {
805                 anchor.addClass('shutter_closed');
806                 
807                 $.post(post_url, { section_name: anchor.attr('pgsec_name'), new_section_state: 'true' });
808             });
809
810             anchor.bind('ncbitoggleropen', function() {
811                 anchor.removeClass('shutter_closed');
812                 $.post(post_url, { section_name: anchor.attr('pgsec_name'), new_section_state: 'false' });
813             });
814
815         });  // end each loop
816         
817         /* Popper for brieflink */
818         $('li.brieflinkpopper').each( function(){
819             var $this = $( this );
820             var popper = $this.find('a.brieflinkpopperctrl') ;
821             var popnode = $this.find('div.brieflinkpop');
822             var popid = popnode.attr('id') || $.ui.jig._generateId('brieflinkpop');
823             popnode.attr('id', popid);
824             popper.ncbipopper({
825                 destSelector: "#" + popid,
826                 destPosition: 'top right', 
827                 triggerPosition: 'middle left', 
828                 hasArrow: true, 
829                 arrowDirection: 'right',
830                 isTriggerElementCloseClick: false,
831                 adjustFit: 'none',
832                 openAnimation: 'none',
833                 closeAnimation: 'none',
834                 delayTimeout : 130
835             });
836         }); // end each loop
837     });// end on page ready
838 })( jQuery );
839 ;
840 (function( $ ){ // pass in $ to self exec anon fn
841     // on page ready
842     $( function() {
843     
844         // Initialize popper
845         $('li.ralinkpopper').each( function(){
846             var $this = $( this );
847             var popper = $this;
848             var popnode = $this.find('div.ralinkpop');
849             var popid = popnode.attr('id') || $.ui.jig._generateId('ralinkpop');
850             popnode.attr('id', popid);
851             popper.ncbipopper({
852                 destSelector: "#" + popid,
853                 destPosition: 'top right', 
854                 triggerPosition: 'middle left', 
855                 hasArrow: true, 
856                 arrowDirection: 'right',
857                 isTriggerElementCloseClick: false,
858                 adjustFit: 'none',
859                 openAnimation: 'none',
860                 closeAnimation: 'none',
861                 delayTimeout : 130
862             });
863         }); // end each loop
864         
865     });// end on page ready
866
867 })( jQuery );
868
869
870 function historyDisplayState(cmd)
871 {
872     var post_url = '/myncbi/section-state/';
873
874     if (cmd == 'ClearHT')
875     {
876         if (!confirm('Are you sure you want to delete all your saved Recent Activity?'))
877         {
878             return;
879         }
880     }
881
882     var ajax_request = jQuery.post(post_url, { history_display_state: cmd })
883         .complete(function(jqXHR, textStatus) {    
884         
885             var htdisplay = jQuery('#HTDisplay');
886             var ul = jQuery('#activity');
887
888             if (cmd == 'HTOn') 
889             { 
890                 // so that the following msg will show up
891                 htdisplay.removeClass();
892                 
893                 if (jqXHR.status == 408) 
894                 { 
895                     htdisplay.html("<p class='HTOn'>Your browsing activity is temporarily unavailable.</p>");
896                     return;
897                 }
898                 
899                 if (htdisplay.find('#activity li').length > 0)
900                 {
901                     ul.removeClass('hide');    
902                 }
903                 else
904                 {
905                     htdisplay.addClass('HTOn');
906                 }
907                 
908             }         
909             else if (cmd == 'HTOff') 
910             {                         
911                 ul.addClass('hide'); 
912                 htdisplay.removeClass().addClass('HTOff');    // make "Activity recording is turned off." and the turnOn link show up             
913             }
914             else if (cmd == 'ClearHT') 
915             { 
916                 if (htdisplay.attr('class') == '') 
917                 {                 
918                     htdisplay.addClass('HTOn');  // show "Your browsing activity is empty." message                                  
919
920                     ul.removeClass().addClass('hide'); 
921                     ul.html('');
922                 }
923             } 
924         });
925
926 }
927
928
929 ;
930 /*
931   PMCVCitedRefBlocksJS
932   
933   This code is adapted from the JS that Aaron Cohen wrote, in PPMCArticlePageJS.
934   I (cfm) use the term CRB (cited reference block) to refer to the portlets on
935   an article page that are aligned with the paragraphs.
936   
937   This module performs two main functions:
938   - Initializes and manages the CRBs, and the links within them.
939     These come as div.pmc_para_cit elements from the backend.
940   - Initializes and manages the poppers on the links within the
941     body of the article (called body links).  There are two types:
942       - Body links that have a corresponding CRB link.  These will,
943         for the most part, correspond to citations to articles that
944         are in PubMed. In these cases, the popper text gets cloned
945         from the CRB links.
946       - Body links that do not have a corresponding CRB link.  These
947         are mostly citations that don't have a corresponding PubMed
948         entry.  In these cases, the popper text gets cloned from the
949         references section.
950
951   CRB initial state:
952   
953     The CRBs should all be rendered on the page in their fully-expanded
954     states, in the discovery column.  They should appear below any "fixed"
955     portlets -- which are portlets of the normal kind, that appear 
956     one-after-another at the top of the disco column.  They should have no
957     special positioning.
958
959     They should all have the class pmc_para_cit.  In the CSS, we initially
960     set the visibility of these to "hidden", to prevent the flash of unstyled
961     content.
962       
963   For reference, here are the jQuery data items that we save with the
964   DOM elements:
965   
966     - CRB (div.pmc_para_cit)
967         - myParagraph - reference to the jQuery wrapper object for the
968           paragraph corresponding to this CRB.
969         - myTop - integer - top of this CRB.  This is constant and is
970           set during initialization.  If there's no para corresponding
971           to this CRB (edge case), then this will be 0.
972         - expandedHeight - int
973         - collapsedHeight - int
974         - collapsedNumLinks - int
975         - isCollapsed - boolean
976         - autoCollapseTimerId - int
977
978     - Links in a CRB (div.pmc_para_cit li):
979         - myCrb - ref. to the jQuery object for this link's containing CRB.
980         - expandedHeight - int
981         - myBodyLinks - reference to the jQuery object that holds the
982           (one-to-many) body links that correspond to the *same citation*
983           as this CRB link.  Note that this is *not* the complement of the
984           body link's myCrbLink data member.  Here's the difference.  For
985           a body link to have a myCrbLink pointing to this CRB link, it 
986           must be in the paragraph corresponding to this CRB.  But, the
987           CRB link's myBodyLinks lists *all* of the links in the body,
988           regardless of paragraph, that correspond to the same citation.
989           
990         - isExtra - boolean.
991         - isCollapsed - boolean
992
993     - Body links:
994         - myCrbLink - reference to the jQuery object for the
995           CRB <li> corresponding to this citation link in the body.
996         - ncbipopper - reference to the jQuery object for this link's
997           popper.
998 */
999
1000
1001 // We use an IIFE in order to not pollute the global namespace.
1002 ( function($j) {
1003
1004     // This constant can be set to control the maximum number of
1005     // links ever to show in a CRB.  But note that this is already 
1006     // coded in the xslt that generates the portlet on the server.
1007     // So we probably don't need this.  Anyway, it might be nice to
1008     // have.  So I'll keep it and set it to a high value.
1009     var MAX_LINKS_PER_CRB = 50;
1010
1011     // Specify a time delay before the CRB will "auto-collapse".
1012     var AUTOCOLLAPSE_TIMEOUT = 4000; 
1013
1014     // Attach the initialize() function to the window load event.
1015     // I used to think this was only necessary for Safari, but see 
1016     // PMC-14368, and this SE post:  
1017     // http://stackoverflow.com/questions/544993/official-way-to-ask-jquery-wait-for-all-images-to-load-before-executing-somethin
1018     $j(window).load(initialize);
1019
1020     //-------------------------------------------------------------
1021     // This initialize function runs when the DOM is ready.
1022     // All the other function definitions are nested inside this one,
1023     // so that they close over the local variables that are initialized
1024     // here.
1025     
1026     function initialize() {
1027         
1028         // Get the list of all CRBs in the document
1029         var crbs = $j('div.pmc_para_cit');
1030         var numCrbs = crbs.length;
1031
1032
1033         // We need to hide all CRBs that interfere with one of the main 
1034         // discovery portlets.  We'll assume that these main portlets all 
1035         // appear at the top.  So we have to find out the bottom-most 
1036         // boundary of all of these.  This calculation is in a function,
1037         // because it gets re-computed every time one of those fixed
1038         // portlets expands or collapses.
1039
1040         var fixedPortlets = $j('div.portlet').not('div.pmc_para_cit');
1041         var mainPortletsBottom = getMainPortletsBottom();
1042         
1043         // Attach a handler to all of these fixed portlets, that gets activate
1044         // whenever a shutter button clicks.  Since the shutter button click
1045         // causes the size to change, we have to re-hide/show CRBs as necessary.
1046         fixedPortlets.find("a.portlet_shutter")
1047             .click(fixedPortletShutterClick);
1048
1049         // These will store the values of the inner and outer height
1050         // of a 'collapsed' (two_line) link.  But note that since the links
1051         // are initially in the expanded state, we have to defer grabbing these
1052         // values until the first time we collapse one of them.  
1053         // (linkHeight == -1) marks that we haven't gotten the values yet.
1054         var linkHeight = -1;
1055         var linkOuterHeight;
1056         
1057         // This points to the current expanded CRB, or to null if there isn't one.
1058         var currentExpandedCrb = null;
1059
1060         // Set the hoverintent mouseover event handlers for all <li>s.
1061         // I tweaked sensitivity and interval so that it's not quite so 
1062         // sensitive -- I think otherwise users are likely to get annoyed.
1063         crbs.find('li').hoverIntent({
1064             over: hoverOverCrbLink, 
1065             out: hoverOutCrbLink,
1066             sensitivity: 1,
1067             interval: 150
1068         });
1069
1070         // Set a mouse-over event on all CRBs to control an automatic timer.
1071         // The timer causes the CRB to re-collapse if the user hasn't had her
1072         // mouse over it for a while.
1073         crbs.mouseover(clearAutoCollapseTimerHandler);
1074         crbs.mouseleave(setAutoCollapseTimerHandler);
1075
1076         // Set up the CRB loop driver.  The CRBs are initialized a few at a time in
1077         // a loop that's called once a millisecond.  This progressive rendering
1078         // improves responsiveness when the page first loads.
1079         
1080         // This is a pointer into the array of CRBs, that's used by the driver loop.
1081         var crbNum = 0;
1082         // How many CRBs to process with each iteration of the driver loop.
1083         var CRB_BATCH_SIZE = 3;
1084
1085         // Next add poppers for every link in the body of the text.  This also
1086         // uses a driver loop.  Set it up here.  
1087         
1088         // We're interested in all links that have 'bibr' class, and that
1089         // have a @rid attribute.
1090         var allBodyLinks = $j('a.bibr[rid]');
1091         var numBodyLinks = allBodyLinks.length;
1092         // This acts as a pointer into the array of links, that's used by the
1093         // driver loop.
1094         var bodyLinkNum = 0;
1095         // How many links to process with each iteration of the driver loop.
1096         var BODY_LINK_BATCH_SIZE = 10;
1097         // This is the container div for the poppers that we'll create.  This
1098         // should be present in the document from the server.
1099         var blPopperDiv = $j('#body-link-poppers');
1100         
1101         // We'll keep track of all the poppers we create, indexed by the @rid
1102         // value of the link.  That way, we're reuse duplicate poppers.
1103         var blPoppers = {};
1104
1105         // The driver loops are chained together.  The order should not matter.
1106         // At the end of both driver loops, then we set hoverIntent event handlers 
1107         // on the body links.  This step must be performed last.  That's because 
1108         // both the ncbipopper (set up by the body link driver loop) and the CRB 
1109         // functionality (which sets hover events to highlight the text and the 
1110         // corresponding links in the CRB) use hoverIntent; so the events 
1111         // conflict.  To fix it, I put calls to the ncbipopper open() and 
1112         // close() methods at the end of the CRB hover event handlers, thus 
1113         // chaining the handlers together. 
1114         crbDriverLoop();
1115
1116
1117         //-----------------------------------------------------------------
1118         // Here is the CRB driver loop, which handles a few CRBs, and then
1119         // queues itself up to execute again after one ms.
1120         function crbDriverLoop() {
1121             for (var i = 0; i < CRB_BATCH_SIZE; ++i) {
1122                 if (crbNum >= numCrbs) break;
1123                 positionOneCrb($j(crbs[crbNum]));
1124                 crbNum++;
1125             }
1126             // Set up to call ourselves after one millisecond
1127             if (crbNum < numCrbs) {
1128                 setTimeout(crbDriverLoop, 1);
1129             }
1130             // When this driver loop is done, it daisy-chains to the
1131             // body-link loop
1132             else {
1133                 setTimeout(bodyLinkDriverLoop, 1);
1134             }
1135         };
1136
1137         //-----------------------------------------------------------------
1138         // This is a similar driver loop for setting the poppers on all
1139         // of the body links.
1140         function bodyLinkDriverLoop() {
1141             for (var i = 0; i < BODY_LINK_BATCH_SIZE; ++i) {
1142                 if (bodyLinkNum >= numBodyLinks) break;
1143                 addBodyLinkPopper($j(allBodyLinks[bodyLinkNum]));
1144                 bodyLinkNum++;
1145             }
1146             // Set up to call ourselves after one millisecond
1147             if (bodyLinkNum < numBodyLinks) {
1148                 setTimeout(bodyLinkDriverLoop, 1);
1149             }
1150             
1151             // When this driver loop is done, it invokes a function that
1152             // sets all of the custom hoverIntent handlers on the body links.
1153             
1154             // PMC-15007 - Took this out.  It was causing the popper to go away whenever
1155             // you mouseout of the trigger link.  This was causing the flashing problem
1156             // described in that ticket.  But worse, it was preventing you from being able
1157             // to mouse over the popper without the popper going away.
1158             else {
1159                 //setTimeout(setBodyLinkHoverHandlers, 1);
1160             }
1161         }
1162
1163         //-----------------------------------------------------------------
1164         // This sets the hoverIntent handlers on all body links.  As mentioned
1165         // above, this must be done last.
1166         // PMC-15007 - this is not called.  See above.
1167         function setBodyLinkHoverHandlers() {
1168             $j('a.bibr[rid]')
1169                 .hoverIntent({
1170                     over: hoverOverBodyLink, 
1171                     out: hoverOutBodyLink,
1172                     sensitivity: 1,
1173                     interval: 150
1174                 });
1175         }
1176
1177         //-----------------------------------------------------------------
1178         // This function absolutely positions one of the CRBs.  For progressive-
1179         // rendering, this gets called from the driver loop above.
1180         function positionOneCrb(jcrb)
1181         {
1182             // Get the paragraph to which this CRB corresponds
1183             var rid = jcrb.attr('rid');
1184             var p = $j('#' + rid);
1185             // If there is no paragraph, just hide the CRB and return
1186             if (p.length != 1) {
1187                 jcrb.hide();
1188                 jcrb.data('myTop', 0);
1189                 return;
1190             }
1191             // Record this paragraph
1192             jcrb.data('myParagraph', p);
1193
1194
1195             // We'll compute the "visual top", which takes into account the
1196             // reported top, the top margin, and a fudge factor.
1197             var myTop = parseInt(p.position().top) + parseInt(p.css('marginTop')) - 7;
1198             jcrb.data('myTop', myTop);
1199
1200             // Do some work for each link within this CRB
1201             var crbLinks = jcrb.find('li');
1202             crbLinks.each(function() {
1203                 var li = $j(this);
1204
1205                 // Get the expanded heights of each of the <li>s.
1206                 var h = li.height();
1207
1208
1209                 // Find the matching links in the body (might be several)
1210                 var referenceId = li.attr('reference_id');
1211                 var bodyLinks = p.find('a.bibr[rid="' + referenceId + '"]');
1212                 
1213                 // Set a data pointer from the body links back to this CRB link
1214                 bodyLinks.data('myCrbLink', li);
1215                 
1216                 // Now find myBodyLinks.  Note that this is *not* the same as the
1217                 // bodyLinks above.  Here's the difference.  For
1218                 // a body link to have a myCrbLink pointing to this CRB link, it 
1219                 // must be in the paragraph corresponding to this CRB.  But, the
1220                 // CRB link's myBodyLinks lists *all* of the links in the body,
1221                 // regardless of paragraph, that correspond to the same citation.
1222                 var myBodyLinks = $j('a.bibr[rid="' + referenceId + '"]');
1223
1224                 // Store these data in the CRB link
1225                 li.data({
1226                     'myCrb': jcrb,   // reference from link back to containing CRB
1227                     'expandedHeight': h,
1228                     'myBodyLinks': myBodyLinks
1229                 }); 
1230             });
1231
1232             // Now collapse the CRB into its default resting state
1233             
1234             // First collapse each of the <li>s by adding the 'two_line' class.
1235             crbLinks.each(function() {
1236                 var li = $j(this);
1237                 li.removeClass('expanded')
1238                   .removeClass('highlight')
1239                   .addClass('two_line');
1240                 li.data('isCollapsed', true);
1241                 
1242                 // If this is the first-ever 'two_line' link that we've seen, 
1243                 // then record its heights
1244                 if (linkHeight == -1) {
1245                     linkHeight = li.height();
1246                     linkOuterHeight = li.outerHeight(true);
1247                 }
1248             });
1249
1250             // Get the height of the paragraph.  This gives the maximum height
1251             // of the 'collapsed' CRB.  In other words, in the collapsed state,
1252             // the CRB should not extend below the bottom of the para.
1253             var pHeight = p.height();
1254
1255             // The number of links in this CRB, as delivered from the server.
1256             var numLinks = crbLinks.length;
1257
1258             // Calculate how many links will fit on this CRB, according to the
1259             // height of the paragraph.
1260             // Note that this is a little bit tricky.  When we figure out whether
1261             // or not we need to hide links, and, as a consequence, add the "see more"
1262             // link, then we *do not* take the height of the "see more" link into
1263             // account.  But, once we know that we need to hide links, and thus add
1264             // the "see more" link, then we need to take the height of that link into
1265             // account to figure out *how many links to hide*.
1266             var linksFit = Math.floor( pHeight / linkOuterHeight );
1267
1268             // Maximum number of links to show here is based either on policy
1269             // (MAX_LINKS_PER_CRB) or on the number that will fit.
1270             var maxLinks = Math.min(MAX_LINKS_PER_CRB, linksFit);
1271             
1272             // See if we need to hide some.
1273             if (numLinks > maxLinks) {
1274                 // We need to take off some links.  But first, let's add the "more"
1275                 // link, and measure the total expanded height.
1276
1277                 // Add the "more" link.  This gets appended as the next sibling of
1278                 // the <ul>.  Measure the height before and after, so we know how
1279                 // much height this link contributes.
1280                 var heightBefore = jcrb.height();
1281
1282                 var moreLink = $j("<a href='' class='seemore'>See more ...</a>");
1283                 jcrb.find('div.portlet_content').append(moreLink);
1284                 
1285                 // Add the event handler.
1286                 moreLink.click(moreLinkClick);
1287                 
1288                 // Measure the expanded height
1289                 var expandedHeight = jcrb.height();
1290                 
1291                 // Figure out how much the "see more" link added
1292                 var moreLinkHeight = jcrb.height() - heightBefore;
1293                 
1294                 // Based on this new information, figure out how many links we will
1295                 // show (never go below 0).
1296                 var linksToShow = 
1297                     Math.max(0, Math.floor( (pHeight - moreLinkHeight) / linkOuterHeight ));
1298
1299                 // Now hide the excess links.  For each, record that it is an extra.
1300                 for (var i = linksToShow; i < numLinks; i++) {
1301                     $j(crbLinks[i])
1302                         .hide()
1303                         .data('isExtra', true);
1304                 }
1305
1306                 // If we're showing no links, change "See more ..." to "See links ..."
1307                 if (linksToShow <= 0) moreLink.text("See links ...");
1308
1309                 // Measure the height again and save as collapsedHeight
1310                 var collapsedHeight = jcrb.height();
1311
1312                 // Now store a bunch of this data with the CRB DOM object
1313                 jcrb.data({
1314                     'expandedHeight': expandedHeight,
1315                     'collapsedHeight': collapsedHeight,
1316                     'collapsedNumLinks': linksToShow
1317                 });
1318             }
1319
1320             // Whether or not we need to hide any links, initialize
1321             // the isCollapsed flag, and the autoCollapseTimerId.
1322             jcrb.data({
1323                 'isCollapsed': true,
1324                 'autoCollapseTimerId': -1
1325             });
1326
1327             // Capture the width prior to repositioning.  Since we're changing
1328             // from position-static to position-absolute, we'll need to set
1329             // this to a fixed value.
1330             var width = jcrb.css('width');
1331
1332             // The final thing we do is to position it next to the para,
1333             // and make it visible.
1334             jcrb.css( {
1335                 'position': 'absolute',
1336                 'visibility': 'visible',
1337                 'width': width,
1338                 'top': myTop 
1339             });
1340
1341             // If it would interfere with one of the fixed portlets, hide it.
1342             if (myTop <= mainPortletsBottom) jcrb.hide();
1343         }
1344
1345         //-----------------------------------------------------------------
1346         // This function handles the mouseover event when the user hovers 
1347         // over one of the CRB links items.
1348         function hoverOverCrbLink(event) {
1349             //console.info("hoverOverCrbLink");
1350             var link = $j(this);
1351             // Call expandLink with checkHidden false - we know its not 
1352             // hidden, because the user has his mouse over it.
1353             expandLink(link, false);
1354         }
1355
1356         //-----------------------------------------------------------------
1357         // This gets called to highlight a particular link in a CRB.
1358         // It checkHidden is true, then this first checks to see if this
1359         // link's CRB is collapsed, and this link is hidden.  If so, then
1360         // this first expands the CRB.  
1361         // [Note that the 'checkHidden' feature is not used.  We originally
1362         // implemented this so that if the user hovers over a body link
1363         // corresponding to an "extra" CRB link, then that would cause the
1364         // CRB to expand to show it.  But David didn't like so much animation
1365         // going on in the disco column while the user is moving his mouse
1366         // around over the body.  Even though it's not used, I left the code in.]
1367         
1368         function expandLink(link, checkHidden) {
1369             //console.info("expandLink");
1370             var jcrb = link.data('myCrb');
1371             jcrb.addClass("stretched");
1372             // Set the z-index.  This ensures that when the CRBs are expanded, they
1373             // are layered on top of ones that are collapsed.
1374             jcrb.css('z-index', 1);
1375             
1376             // Enforce the policy that only one CRB is stretched at a time.
1377             collapseOtherCrbs(jcrb);
1378             
1379             // See if we need to expand the whole CRB (equivalent to "see more").
1380             if ( checkHidden && 
1381                  link.data('isExtra') &&
1382                  jcrb.data('isCollapsed') )
1383             {
1384                 expandCrb(jcrb);
1385             }
1386             
1387             var h = link.data('expandedHeight');
1388             link.data('isCollapsed', false);
1389
1390             // Animate the height change.  Whenever we do this, we have
1391             // to specify the exact numeric value of the final height.
1392             // When done, we set the CSS property back to 'auto'.
1393             link.animate(
1394                 {'height': h},
1395                 { 'complete': function() {
1396                     jcrb.css({height: "auto"});
1397                   },
1398                   'duration': 'fast'
1399                 }
1400             );
1401             
1402             // Do this *after* kicking off the animation, because this is
1403             // what causes the actual height of the link to change.
1404             link.removeClass('two_line')
1405                 .addClass('expanded');
1406             
1407             // This causes the link in the CRB, and all its corresponding
1408             // links in the body, to be highlighted with a special color.
1409             highlightLink(link);
1410         }
1411
1412
1413         //-----------------------------------------------------------------
1414         // This function handles the event when the user moves the mouse 
1415         // out of the <li> items.
1416         function hoverOutCrbLink(event) {
1417             //console.info("hoverOutCrbLink");
1418             var link = $j(this);
1419             unexpandLink(link);
1420         }
1421
1422         //-----------------------------------------------------------------
1423         function unexpandLink(link) {
1424             //console.info("unexpandLink");
1425             var jcrb = link.data('myCrb');
1426
1427             link.removeClass('expanded')
1428                 .addClass('two_line');
1429             // Remove the highlight color from the link in the CRB and
1430             // all the corresponding links in the body.
1431             unhighlightLink(link);
1432             
1433             link.animate(
1434                 {'height': linkHeight},
1435                 { 'complete': function() {
1436                       link.data('isCollapsed', true);
1437                       // Check if we still need the 'stretched' class on the 
1438                       // CRB parent.
1439                       checkStretched(jcrb);
1440                       // Reset the height property to auto.
1441                       jcrb.css({height: "auto"});
1442                       
1443                   },
1444                   'duration': 'fast'
1445                 }
1446             );
1447         }
1448         
1449         //-----------------------------------------------------------------
1450         // This function handles the hovering over a body link.  Note that
1451         // some of these will have corresponding CRB links, and some will
1452         // not.
1453         function hoverOverBodyLink(event) {
1454             //console.info("hoverOverBodyLink");
1455             var bodyLink = $j(this);
1456
1457           /* PMC-15150 - reduce "christmas tree effect", don't highlight
1458             CRB when user hovers over body link.
1459             
1460             var crbLink = bodyLink.data('myCrbLink');
1461             if (crbLink) {
1462                 highlightLink(crbLink);
1463                 var jcrb = crbLink.data('myCrb');
1464                 clearAutoCollapseTimer(jcrb);
1465             }
1466           */
1467             
1468             // If there's a popper associated with this, open it.
1469             // Both this and JIG's ncbipopper use hoverIntent, so we have
1470             // to chain these events together.
1471             var popper = bodyLink.data('ncbipopper');
1472             if (popper) popper.open();
1473         }
1474
1475         //-----------------------------------------------------------------
1476         function hoverOutBodyLink(event) {
1477             //console.info("hoverOutBodyLink");
1478             var bodyLink = $j(this);
1479             var crbLink = bodyLink.data('myCrbLink');
1480             if (crbLink) {
1481                 unhighlightLink(crbLink);
1482                 var jcrb = crbLink.data('myCrb');
1483                 setAutoCollapseTimer(jcrb);
1484             }
1485             
1486             // If there's a popper associated with this, close it
1487             var popper = bodyLink.data('ncbipopper');
1488             if (popper) popper.close();
1489         }
1490
1491         //-----------------------------------------------------------------
1492         // This function highlights a CRB link, and all of the body
1493         // links that correspond to it.
1494         function highlightLink(link) {
1495             //console.info("highlightLink");
1496             link.addClass('highlight');
1497             link.data('myBodyLinks').addClass('highlight');
1498         }
1499
1500         //-----------------------------------------------------------------
1501         // This function unhighlights a CRB link, and all of the body links
1502         // that correspond to it.
1503         function unhighlightLink(link) {
1504             //console.info("unhighlightLink");
1505             link.removeClass('highlight');
1506             link.data('myBodyLinks').removeClass('highlight');
1507         }
1508
1509         //-----------------------------------------------------------------
1510         // This function removes, when necessary, the 'stretched' class on 
1511         // the CRB object.  If the jcrb isCollapsed is true, and all of
1512         // the links' isCollapsed are true, then clear this class.  Note 
1513         // that we don't check whether or not to *set* the class - that is 
1514         // done instantly whenever any of those things starts to expand.
1515         function checkStretched(jcrb) {
1516             //console.info("checkStretched");
1517             if (!jcrb.data('isCollapsed')) return;
1518             
1519             var hasExpandedLink = false;
1520             jcrb.find('li').each(function() {
1521                 if (!$j(this).data('isCollapsed')) hasExpandedLink = true;
1522             });
1523             if (hasExpandedLink) return;
1524             
1525             jcrb.removeClass('stretched');
1526             jcrb.css('z-index', 0);
1527         }
1528         
1529         //-----------------------------------------------------------------
1530         // Click handler for the "see more" link at the bottom of the portlet.
1531         // Note that this might be either a "see more" or a "see less" click.
1532         function moreLinkClick(event) {
1533             //console.info("moreLinkClick");
1534             var moreLink = $j(this);
1535             var jcrb = moreLink.parents('div.pmc_para_cit');
1536
1537             // If we're collapsed, then we need to expand
1538             if (jcrb.data('isCollapsed')) {
1539                 expandCrb(jcrb);
1540             }
1541             else {
1542                 collapseCrb(jcrb);
1543             }
1544
1545             // Override the default action
1546             return false;
1547         }
1548         
1549         //-----------------------------------------------------------------
1550         // This function animates the expansion of a CRB to show all of the
1551         // links.  We will never call this unless this is a collapsible CRB.
1552         // (In other words, if all the links fit within the height of the
1553         // paragraph, then we'll guarantee that we'll never call this 
1554         // function.)
1555         
1556         function expandCrb(jcrb) {
1557             //console.info("expandCrb");
1558             
1559             // Make sure this CRB is now on top
1560             collapseOtherCrbs(jcrb);
1561
1562             // Update the state variable
1563             jcrb.data('isCollapsed', false);
1564             currentExpandedCrb = jcrb;
1565         
1566             // Add the 'stretched' class to the outer div
1567             jcrb.addClass("stretched");
1568             jcrb.css('z-index', 1);
1569             
1570             // Change the label on the "more" link
1571             jcrb.find('.seemore').text("See less ...");
1572             
1573             // Animate the height change.
1574             var d = jcrb.data();
1575             jcrb.animate(
1576                 {'height': d.expandedHeight},
1577                 { 'complete': function() {
1578                       jcrb.css({height: "auto"});
1579                   },
1580                   'duration': 'fast'
1581                 }
1582             );
1583
1584             // Show all the links.  This is done *after* we kick off
1585             // the animation because this is the step that causes 
1586             // the actual height to change.
1587             var crbLinks = jcrb.find('ul li.two_line');
1588             var totalNumLinks = crbLinks.length;
1589             for (var i = d.collapsedNumLinks; i < totalNumLinks; ++i) {
1590                 $j(crbLinks[i]).show();
1591             }
1592         }
1593         
1594         //-----------------------------------------------------------------
1595         // This function animates the collapse of the CRB into it's default 
1596         // resting state, including ensuring that all child links are in 
1597         // their two_line, collapsed state.  This gets called from two 
1598         // places: when the user clicks on "see less", and when the 
1599         // auto-collapse timer triggers.
1600         
1601         function collapseCrb(jcrb) {
1602             //console.info("collapseCrb");
1603             
1604             var d = jcrb.data();
1605
1606             // Change the label on the link
1607             var linkText = d.collapsedNumLinks > 0 ? "See more ..." : "See links ...";
1608             jcrb.find('.seemore').text(linkText);
1609
1610             // Get the link children
1611             var crbLinks = jcrb.find('ul li');
1612             var totalNumLinks = crbLinks.length;
1613
1614             // Animate the height change.  We use the 'complete' option of 
1615             // the animation function to hide all of the excess links after
1616             // the animation is done.  If we hid them all right away, the 
1617             // height would change abruptly (no animation).
1618             jcrb.animate(
1619                 { 'height': d.collapsedHeight },
1620                 { 'complete': function() {
1621                       // For all links
1622                       crbLinks.each(function() {
1623                           var li = $j(this);
1624                           
1625                           // Put the link into its collapsed state
1626                           li.removeClass('expanded')
1627                             .removeClass('highlight')
1628                             .addClass('two_line');
1629                           li.data('isCollapsed', true);
1630
1631                           // Hide 'overflow' links
1632                           if (li.data('isExtra')) li.hide();
1633                       });
1634                       
1635                       // Now put the CRB object itself into the collapsed state
1636                       jcrb.data('isCollapsed', true);
1637                       jcrb.removeClass('stretched');
1638                       jcrb.css('z-index', 0);
1639                       
1640                       // Reset the height property to auto.
1641                       jcrb.css({height: "auto"});
1642                   },
1643                   'duration': 'fast'
1644                 }
1645             );
1646             
1647             currentExpandedCrb = null;
1648         }
1649
1650         //-----------------------------------------------------------------
1651         // This function sets a timer on a CRB object so that it will
1652         // reclose after a fixed period of time after the last time that
1653         // the user has moved his/her mouse out of the div.  This is set 
1654         // up as a handler for mouseleave events.  Note that it the CRB 
1655         // is already collapsed, this does nothing.
1656
1657         // Handler for the mouse-out event for the whole CRB div.
1658         function setAutoCollapseTimerHandler(event) {
1659             //console.info("setAutoCollapseTimerHandler");
1660             setAutoCollapseTimer($j(this));
1661         }
1662         
1663         // This does the work.  It is also called from the handler for 
1664         // the mouse-out event on a corresponding body link.
1665         function setAutoCollapseTimer(jcrb) {        
1666             //console.info("setAutoCollapseTimer");
1667             if (jcrb.data('isCollapsed')) return;
1668             
1669             // Set the timer
1670             var timerId = window.setTimeout(function() { 
1671                 collapseCrb(jcrb); 
1672             }, AUTOCOLLAPSE_TIMEOUT);
1673             
1674             // Record it as a data attribute on this object
1675             jcrb.data('autoCollapseTimerId', timerId);
1676         }
1677
1678         //-----------------------------------------------------------------
1679         // This function clears the auto-collapse timer.  It is set up as
1680         // a handler for the mouseover event.
1681         
1682         // Handler for the mouse-over event for the whole CRB div.
1683         function clearAutoCollapseTimerHandler(event) {
1684             //console.info("clearAutoCollapseTimerHandler");
1685             clearAutoCollapseTimer($j(this));
1686         }
1687         
1688         // This does the work.  It is also called from the handler for 
1689         // the mouse-over event on a corresponding body tag.
1690         function clearAutoCollapseTimer(jcrb) {
1691             //console.info("clearAutoCollapseTimer");
1692             var timerId = jcrb.data('autoCollapseTimerId');
1693             if (timerId == -1) return;
1694             
1695             window.clearTimeout(timerId);
1696             jcrb.data('autoCollapseTimerId', -1);
1697         }
1698
1699         //-----------------------------------------------------------------
1700         // This function is used to collapse all of the *other* CRBs.
1701         // It is invoked whenever we highlight a link or expand a CRB.
1702         // This makes sure that the focus is always on only one at a time.
1703         function collapseOtherCrbs(jcrb) {
1704             //var cstr = (currentExpandedCrb) ?
1705             //    (", currentExpandedCrb = " + currentExpandedCrb.attr('id')) :
1706             //    "";
1707             //console.info("collapseOtherCrbs; jcrb = " + jcrb.attr('id') + cstr);
1708             
1709             /* This used to loop through all of the CRBs, collapsing all of
1710               them except myId.  But now I just keep track of the (at most
1711               one) CRB that's currently expanded, and collapse that. 
1712             var myId = jcrb.attr('id');
1713
1714             crbs.each(function() {
1715                 var thisCrb = $j(this);
1716                 if (thisCrb.attr('id') != myId) {
1717                     collapseCrb(thisCrb);
1718                 }
1719             });
1720             */
1721             //console.info("Checking, currentExpandedCrb = " + currentExpandedCrb);
1722             if (currentExpandedCrb && currentExpandedCrb.attr('id') != jcrb.attr('id')) {
1723                 collapseCrb(currentExpandedCrb);
1724             }
1725         }
1726
1727         //-----------------------------------------------------------------
1728         // This function is set as an event handler when the user clicks
1729         // on any of the shutter buttons on any of the fixed portlets at the
1730         // top.  When these fixed portlets change size, we have to go
1731         // through all the CRBs again and hide/show depending on whether
1732         // or not they are interfering.
1733         // But, because the portlet size change is animated, the new size
1734         // won't be known for a while, so this in turn sets a timer that
1735         // invokes hideInterfering().
1736         
1737         function fixedPortletShutterClick(event) {
1738             setTimeout(hideInterfering, 200);
1739         }
1740         
1741         //-----------------------------------------------------------------
1742         // hideInterfering iterates through all of the CRBs and hides any
1743         // that would interfere with any of the fixed portlets at the top.
1744         // It is called every time the user clicks a shutter button on one 
1745         // of the fixed portlets.
1746         
1747         function hideInterfering() {
1748             var mainPortletsBottom = getMainPortletsBottom();
1749             crbs.each(function() {
1750                 var jcrb = $j(this);
1751                 if (jcrb.data('myTop') <= mainPortletsBottom) {
1752                     jcrb.hide();
1753                 }
1754                 else {
1755                     jcrb.show();
1756                 }
1757             });
1758         }
1759
1760         //-----------------------------------------------------------------
1761         // This function gets the y-coordinate of the bottom of the lowest
1762         // of the fixed portlets.
1763         
1764         function getMainPortletsBottom() {
1765             var bottom = 0;
1766             fixedPortlets.each(function() {
1767                 var p = $j(this);
1768                 var top = parseInt(p.position().top) + parseInt(p.css('marginTop'));
1769                 var thisBottom = top + p.height();
1770                 bottom = Math.max(bottom, thisBottom);
1771             });
1772             return bottom;
1773         }
1774         
1775         //-----------------------------------------------------------------
1776         // This function adds a popper to one of the links in the body.
1777         // It is called by the driver loop above.  The link is guaranteed
1778         // to have the 'bibr' class, and a @rid attribute.
1779         var popperCount = 0;
1780         function addBodyLinkPopper(link) {
1781             // If we never found a container for our poppers in the source
1782             // document, then we can't do anything.
1783             if (blPopperDiv.length < 1) return;
1784             
1785             var rid = link.attr('rid');
1786             // Some id's from the backend have special characters, so we
1787             // need to escape them whenever they're used as jQuery selectors.
1788             // See PMC-6384 and http://tinyurl.com/2qfqgc.
1789             var ridEscaped = rid.replace(/:/g, "\\:")
1790                                 .replace(/\./g, "\\.")
1791                                 .replace(/·/g, "\\·");
1792
1793             var popperId = "body-link-popper-" + rid; // + "-" + popperCount;
1794             var popperIdEscaped = "body-link-popper-" + ridEscaped; // + "-" + popperCount;
1795             popperCount++
1796
1797             // If we've already made a popper for this particular reference,
1798             // let's use that.
1799             var popper = blPoppers[rid];
1800             
1801             // Otherwise, we need to make one.
1802             if (!popper) {
1803                 //console.info("Adding pooper for body link, rid = " + rid);
1804                 
1805                 popper = $j("<div/>", { 
1806                     'id': popperId,
1807                     'class': 'body-link-popper',
1808                     'style': 'display: none'
1809                 });
1810
1811                 // If this link has a corresponding CRB link, then we'll
1812                 // use that for the popper text
1813                 var myCrbLink = link.data('myCrbLink');
1814                 if (myCrbLink) {
1815                     // Get the popper text from the CRB link
1816                     //console.info("Got a CRB link, rid = " + rid);
1817                     popper.append(myCrbLink.children("a").contents().clone());
1818                     popper.append(myCrbLink.children(".alt-note").children().clone());
1819                     
1820                     // Find the pubmed link, if exists
1821                     var $pmidLink = myCrbLink.find("a[href *= 'pubmed']");
1822                     //console.info("myCrbLink = %o", myCrbLink);
1823                     //console.info("pmidLink = %o", $pmidLink);
1824                     var pubmedLink = $pmidLink.length > 0
1825                         ? "[<a href='" + $pmidLink.attr('href') + "'>PubMed</a>] "
1826                         : "";
1827                     popper.append("<p>" + pubmedLink + "[" + 
1828                         "<a href='" + link.attr("href") + "'>Ref list</a>]</p>");
1829                 }
1830                 
1831                 // Otherwise we'll copy the text from the references section.
1832                 else {
1833                     // Find the text for this popup from the element pointed to by 
1834                     // @rid.  This will be in the reference section of the article.
1835                     //console.info("Not a CRB link, rid = " + rid);
1836                     var refElem = $j('#' + ridEscaped);
1837                     
1838                     // Check to make sure that we found something
1839                     if (refElem.length > 0) {
1840     
1841                         var citeText = refElem.text();
1842                         var popText = $j.trim(citeText);
1843         
1844                         // If the @href element of the current <a> tag (currElem)
1845                         // has a '#' portion, meaning that it is a link to the
1846                         // reference list in this same article, then indicate 
1847                         // that in the popup text.
1848                         if (link.attr("href").charAt(0) == '#') {
1849                             popText += ' [<a href="' + link.attr("href") + '">Ref list</a>]';
1850                         }
1851                     }
1852                     popper.append(popText);
1853                 }
1854                 
1855                 // Put this new popper into the container div.
1856                 blPopperDiv.append(popper);
1857                 
1858                 // And record it for posterity.
1859                 blPoppers[rid] = popper;
1860             }
1861             
1862             // FIXME:
1863             // It would be nice if I could pass the configuration parameters
1864             // to JIG as a JS object; but it doesn't work.
1865             //var popperConfig = {
1866             //    'destSelector': ('#' + popperId) 
1867             //};
1868             // Note that I have to set a fixed width here.  "auto" doesn't work.
1869             // Nor does setting it in the CSS file.
1870             
1871             // FIXME:  This is a little debug feature that lets you enable the
1872             // popper's autoAdjust feature.  Because of JSL-1519, I have it
1873             // turned off by default.
1874             var loc = window.location.href;
1875             var qs = loc.substring(loc.indexOf("?") + 1);
1876             var qsParams = qs.split(/&/);
1877             var numQsParams = qsParams.length;
1878             var gotAutoAdjust = false;
1879             for (var i = 0; i < numQsParams; ++i) {
1880                 if (qsParams[i] == "__autoadjust") gotAutoAdjust = true;
1881             }
1882             //console.info("qsParams = " + qsParams[0] + ", " + qsParams[1]);
1883             gotAutoAdjust = true;
1884             var adjustFitStr = gotAutoAdjust ? 'autoAdjust' : 'none';
1885
1886             link.addClass('jig-ncbipopper')
1887                 .data('jigconfig', { 
1888                     destSelector: "#" + popperIdEscaped,
1889                     isTriggerElementCloseClick: false,
1890                     hasArrow: true, 
1891                     width: "30em", 
1892                     adjustFit: adjustFitStr,
1893                     triggerPosition: "top right"
1894                 });
1895             $j.ui.jig.scan(link);
1896         }
1897     } /* end of initialize() */
1898 })(jQuery);
1899